package org.qloudgen.borg.pages;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.DataGridView;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Check;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.PageableListView;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.qloudgen.borg.CommonValues;
import org.qloudgen.borg.beans.CacheBean;
import org.qloudgen.borg.beans.Cluster;
import org.qloudgen.borg.beans.DataSourceBean;
import org.qloudgen.borg.beans.FileBean;
import org.qloudgen.borg.beans.FuncBean;
import org.qloudgen.borg.beans.KeyValue;
import org.qloudgen.borg.beans.ParamBean;
import org.qloudgen.borg.beans.ResultBean;
import org.qloudgen.borg.messaging.message.TaskMessage;
import org.qloudgen.borg.metadb.DefaultImpl;
import org.qloudgen.borg.pages.common.BasePage;
import org.qloudgen.borg.pages.common.BlankPage;
import org.qloudgen.borg.pages.compnent.CancelButton;
import org.qloudgen.borg.pages.compnent.ParamDropDownChoice;
import org.qloudgen.borg.pages.compnent.ParamTextField;
import org.qloudgen.borg.pages.dataGridView.QueryCellPopulator;
import org.qloudgen.borg.pages.model.FuncProcessModel;
import org.qloudgen.borg.pages.panel.ExportPanel;
import org.qloudgen.borg.result.DataShowOperator;
import org.qloudgen.borg.task.TaskRules;
import org.qloudgen.borg.task.TaskUtils;
import org.qloudgen.borg.utils.CommonUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class AppFuncProcessPage extends BasePage {
	
	static Log log = LogFactory.getLog(AppFuncProcessPage.class);

	private static final long serialVersionUID = 1L;
	private transient List<ParamBean> params = Lists.newArrayList();
	private transient FuncProcessModel model = new FuncProcessModel();
	private transient List<Map<String,String>> list = Lists.newArrayList();
	private transient Map<String,String> map = Maps.newHashMap();
	private transient Map<String,String> clusterMap = Maps.newHashMap();
	private transient Map<String,String> dsMap = Maps.newHashMap();
	private transient String funcNo;
	private transient FuncBean func ;
	private transient FuncProcessModel fpm;
	private transient String cacheID,sql;
	private transient CacheBean cacheBean = new CacheBean();
	private transient List<String> headers = Lists.newArrayList();
	private transient FileBean fileBean = new FileBean();
	
	public AppFuncProcessPage(PageParameters pageParameters){
		
		super();
		this.pp = pageParameters;
		
		// get funcNo from pp
		funcNo = this.pp.get("node").toString();
		
		if (funcNo == null) {
			cacheID = this.pp.get("cacheID").toString();
			funcNo = this.pp.get("funcNo").toString();
			
			// get cache info from ana_cache by cacheID
			cacheBean = DefaultImpl.getCacheInfo(cacheID);
			model.setCluster(cacheBean.getClusterID());
			model.setDataSource(cacheBean.getDsNo());
		}

		func = DefaultImpl.getAppFunc(funcNo);
			
		// form.paramBorder.paramGroup
		final CheckGroup<ParamBean> paramGroup = new CheckGroup<ParamBean>("paramGroup", new ArrayList<ParamBean>());
		paramGroup.setRenderBodyOnly(false).setOutputMarkupId(true);
		
		// add form 
		Form<?> form = new Form("form",new CompoundPropertyModel<FuncProcessModel>(model)) {
			
			private static final long serialVersionUID = 1L;

			@Override
			protected void onSubmit() {
				
				fpm = (FuncProcessModel)getDefaultModelObject();
				List<ParamBean> paramList = (List<ParamBean>) paramGroup.getDefaultModelObject();
				
				if (paramList.size() != params.size()) { info(CommonValues.INFO_FUNC_PARAMS_ALL_SELECT); return;}
				
				StringBuffer sb = new StringBuffer() ;
				int i = 0;
				for(ParamBean param:paramList){
					sb.append(((i == 0 )?"":CommonValues.DEFAULT_FUNC_PARAM_DILIMTER) + param.getParamValue());
					i++;
				}
				
				// query cache
				cacheBean.setClusterID(fpm.getCluster())
						 .setDsNo(fpm.getDataSource())
                         .setFuncNo(funcNo)
                         .setParams(sb.toString())
                         .setReload(fpm.getReload())
                         .setCacheID(null);
				
				if (!TaskRules.isSubmit(cacheBean)) { info(CommonValues.INFO_CAN_NOT_RELOADED); return ;} 
				
				if (cacheBean.isReload()) {
					
					// clear cache
					TaskUtils.clearCache(cacheBean.getFuncNo(), cacheBean.getDsNo(), cacheBean.getClusterID());
					
					// send Task message
					TaskUtils.sendTask(new TaskMessage().setUserNo(CommonValues.getUserNo())
														 .setFuncNo(funcNo)
														 .setClusterID(fpm.getCluster())
														 .setDsNo(fpm.getDataSource())
														 .setParams(sb.toString())
														 .setReload(fpm.getReload()));
					setResponsePage(TaskListPage.class);
					
				} else {
					
					if (TaskRules.isExistsReloadTask(cacheBean)) { info(CommonValues.INFO_EXISTS_RELOAD_TASK); return ;}
					
				}
				
				// if reload is false, then check if has cache
				cacheID = TaskUtils.getCache(cacheBean);
				
				// can not get cache ,so must submit task to queue
				if (cacheID == null ) {
					
					// get concurrent user submit same query
					int taskCount = DefaultImpl.getSameTask(cacheBean);
					
					if (taskCount > 0) { info(CommonValues.INFO_SAME_TASK); return;}
					
					// send Task message
					TaskUtils.sendTask(new TaskMessage().setUserNo(CommonValues.getUserNo())
														 .setFuncNo(funcNo)
														 .setClusterID(fpm.getCluster())
														 .setDsNo(fpm.getDataSource())
														 .setParams(sb.toString())
														 .setReload(fpm.getReload()));
					setResponsePage(TaskListPage.class);
					
				} else {
				
					list.clear();
					list.add(map);
					new DataShowOperator(list).doData(cacheID,sql);
					
				}
			}
			
		};
		this.add(form);
		form.add(paramGroup);
		
		// dataSource
		IChoiceRenderer<Object> dsRenderer = getDsRenderer();
		form.add(new DropDownChoice<String>("dataSource",Lists.newArrayList(dsMap.keySet()),dsRenderer).setNullValid(false).setRequired(true));

		// cluster
		IChoiceRenderer<Object> clusterRenderer = getClusterRenderr();
		form.add(new DropDownChoice<String>("cluster",Lists.newArrayList(clusterMap.keySet()),clusterRenderer).setNullValid(false).setRequired(true));
		
		// param list
		DefaultImpl.getAppFuncParameters(funcNo,params);
		setParamValues();	// if from taskListPage 	
		
		final PageableListView<ParamBean> paramListView = new ParamListView( "rows", params,CommonValues.SHOW_LIST_LARGE_NUMBER);
		paramListView.setOutputMarkupId(true);
		paramGroup.add(paramListView);
		
		// func name
		form.add(new Label("funcName",func.getFuncName()));
		form.add(new Label("limitRows",String.valueOf(CommonValues.getLimitRows())));
		
		/*
		 *  result list 
		 */
		final List<ICellPopulator<Map<String,String>>> columns = new ArrayList<ICellPopulator<Map<String,String>>>();				
		sql = getSql(func.getFuncNo(),headers,columns,map);	
		list.add(map);
		
			// get result data from hadoop
			if (cacheID != null)
				new DataShowOperator(list).doData(cacheID,sql);
			
			final WebMarkupContainer resultList = new WebMarkupContainer("resultList");
			resultList.setOutputMarkupId(true);
			this.add(resultList);
			
			final DataGridView<Map<String,String>> resultRows = new DataGridView<Map<String,String>>("resultRows", columns, new ListDataProvider(list)){
	
				private static final long serialVersionUID = 1L;
	
				protected Item<Map<String,String>> newRowItem(String id, int index, IModel<Map<String,String>> model)
	        	  {
					return (Item<Map<String,String>>) new Item<Map<String,String>>(id, index, model)
							 .add(new AttributeModifier("class",new Model<String>((index ==0)?"title":"default")));
	        	  }
	        };
			resultRows.setOutputMarkupId(true);
			resultList.add(resultRows);
		
		 // file window
		final ModalWindow modal = new ModalWindow("modal");
        this.add(modal);

        modal.setTitle("选择导出路径");
        modal.setCookieName("modal");

        modal.setCloseButtonCallback(new ModalWindow.CloseButtonCallback()
        {
			private static final long serialVersionUID = 1L;

			@Override
            public boolean onCloseButtonClicked(AjaxRequestTarget target)
            {
                return true;
            }
        });
        	
		form.add(new CheckBox("reload"));
		
		// submit
		form.add(new Button ("submit"));
		
		// export
		form.add(new AjaxLink<Void>("export"){

			private static final long serialVersionUID = 1L;

			@Override
			public void onClick(AjaxRequestTarget target) {

				fileBean.setFuncName(func.getFuncName()).setClusterName(clusterMap.get(cacheBean.getClusterID()))
					.setDsName(dsMap.get(cacheBean.getDsNo())).setParams(cacheBean.getParams());
				modal.setContent(new ExportPanel(modal.getContentId(),fileBean,headers,cacheID, sql, modal));
				modal.show(target);
			}});
		
		form.add(new CancelButton("cancel",BlankPage.class));
		
	}

	private void setParamValues() {
		
		if (cacheBean != null && params != null) {
			int i = 1 ;
			for (ParamBean param:params){
				param.setParamValue(CommonUtils.getElement(cacheBean.getParams(), CommonValues.DEFAULT_FUNC_PARAM_DILIMTER, i));
				i++;
			}
		}
	}

	private IChoiceRenderer<Object> getDsRenderer() {

		List<DataSourceBean> dataSources = Lists.newArrayList();
		
		//get dataSources from metadb
		DefaultImpl.getFuncDataSources(funcNo,dataSources);
		dsMap = getDsMap(dataSources);
		
		IChoiceRenderer<Object> dsRenderer = new ChoiceRenderer<Object>() {
			private static final long serialVersionUID = 1L;
			public Object getDisplayValue(Object object) {
				return dsMap.get(object);
			}
		};
		
		return dsRenderer;
	}

	private IChoiceRenderer<Object> getClusterRenderr() {

		List<Cluster> clusters = Lists.newArrayList();
		DefaultImpl.getAppCluster(func.getAppNo(), clusters);
		clusterMap = getClusterMap(clusters);
		
		IChoiceRenderer<Object> clusterRenderer = new ChoiceRenderer<Object>() {
			private static final long serialVersionUID = 1L;
			public Object getDisplayValue(Object object) {
				return clusterMap.get(object);
			}
		};
		
		return clusterRenderer;
	}

	private String getSql(String funcNo,List<String> headers,List<ICellPopulator<Map<String, String>>> columns, Map<String,String> dataMap) {

		List<ResultBean> resultColumns = Lists.newArrayList();
		StringBuffer sb = new StringBuffer(" select ");
		String orderBy = null;
		int i = 1;
		
		// get result column from meta by funcNo
		DefaultImpl.getFuncColumnList(funcNo, resultColumns);
		
		// add seq column
		dataMap.put("row", "序号");
		columns.add(new QueryCellPopulator<Map<String,String>>("row"));
		
		for (ResultBean column:resultColumns){
			
			columns.add(new QueryCellPopulator<Map<String,String>>(column.getColumnCode()));
			dataMap.put(column.getColumnCode(), column.getColumnName());
			headers.add(column.getColumnName());
					
			sb.append( ((i==1)?"":",") + column.getColumnCode());
			
			if (column.getIsOrder().equalsIgnoreCase("y")) {
				orderBy = ((orderBy == null)?"":(orderBy + ",")) +column.getColumnCode();
			}
			
			if (i == resultColumns.size()) {
				sb.append(" from ");
				sb.append(column.getTableName());
				sb.append(" where cache_id = 'null'");
				if (orderBy != null)
					sb.append(" order by " + orderBy);
			}
			
			i++;		
		}
			
		return sb.toString();
	}

	private Map<String, String> getClusterMap(List<Cluster> clusters) {
		Map<String,String> map = Maps.newHashMap();
		for(Cluster cluster:clusters){
			map.put(cluster.getClusterID(), cluster.getClusterName());
		}
		return map;
	}

	private Map<String, String> getDsMap(List<DataSourceBean> dataSources) {
		Map<String,String> map = Maps.newHashMap();
		for(DataSourceBean ds:dataSources){
			map.put(ds.getDsNo(), ds.getDsName());
		}
		return map;
	}
	
	private class ParamListView extends PageableListView<ParamBean>{
		
		private static final long serialVersionUID = 1L;
		
		public ParamListView(String id,	List<ParamBean> model, int itemsPerPage) {
			super(id, model, itemsPerPage);
		}
		
		protected void populateItem(ListItem<ParamBean> item) {
			
			final ParamBean param=(ParamBean) item.getModelObject();
			item.add(new Check("check", item.getModel()){
				private static final long serialVersionUID = 1L;
				protected void onComponentTag(ComponentTag tag)
				{
					super.onComponentTag(tag);
					tag.put("checked", true);
				}}.setEnabled(true));
			item.add(new Label( "paramCode", param.getParamCode()));
			item.add(new Label( "paramName", param.getParamName()));
			
			final FormComponent keyValue ;
			
			switch (param.getParamType()) {
				case 1 : {
					String[] splits = StringUtils.splitByWholeSeparator(param.getParamInfo(),";");
					
					// get array
					String[] values = splits[0].split(",");
					String[] names = splits[1].split(",");
					
					// get map
					Map<String,String> map = Maps.newHashMap();
					for (int i=0; i<values.length; i++)
							map.put(values[i], names[i]);
					
					keyValue = new ParamDropDownChoice("paramValue", new PropertyModel(param,"paramValue"),Lists.newArrayList(values),map); 
					break;
				}
				case 2  :
				default : {
					String[] splits = StringUtils.splitByWholeSeparator(param.getParamInfo(),";");
					
					List<KeyValue> validators = Lists.newArrayList();
					for ( int i=0; i<splits.length; i++){
						String[] validator = StringUtils.splitByWholeSeparator(splits[i],"#");
						validators.add(new KeyValue().setKey(validator[0]).setValue(validator[1]));
					}
					keyValue = new ParamTextField("paramValue", new PropertyModel(param,"paramValue"),validators);
					break;
				}
			}
			
//			keyValue.add(new AjaxFormComponentUpdatingBehavior("onChange") {
//    	  		
//    	  		private static final long serialVersionUID = 1L;
//				@Override
//				protected void onUpdate(AjaxRequestTarget target) {
//					param.setParamValue( (String) keyValue.getDefaultModelObject());
//	    	  	}
//    	  	});
//			keyValue.setDefaultModel(new CompoundPropertyModel(new String("")));
//			keyValue.setModelObject(param.getParamValue());
			
			item.add( keyValue);
			
		}
	}
}
